/*
* Copyright 2009 NOR_/D Labs <http://labs.nortd.com>
*
* This file is part of SceneExpression.
* SceneExpression is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License 
* as published by the Free Software Foundation, either version 3 of 
* the License, or (at your option) any later version. For details
* see <http://www.gnu.org/licenses/>.
*
* * *
* The Scene is one of the most central classes.
* It embodies the viewer, camera, and scene graph root.
* Per application there is usually one instance as part of
* the App derived class of which there is one instance in main().
* Scene and App work together. All the standard events
* (mouse, keyboard, resize) are send from the scene's viewer to
* the App. Latter being the super global, grand-central-like
* event handler and where (the subclass of App) all the user 
* code goes.
*
*/

#ifndef SX_SCENE
#define SX_SCENE


#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osg/Math>
#include <osg/Vec3>

#include <sx/Constants.h>
#include <sx/Types.h>
#include <sx/Visitors.h>
#include <sx/Events.h>

namespace sx {

class Node;
class Widget;



class App : public osgGA::GUIEventHandler,
              public KeyHandler, public MouseHandler, 
              public TouchHandler, public PenHandler,
              public ButtonHandler, public SliderHandler, public CheckboxHandler  {
    public:
  
        Scene*  scene;
  
        App();
        ~App();
        
        virtual void setup() {}
        virtual void update() {}
        virtual void exit() {}
        virtual void resize( int w, int h ) {}

        virtual void keyPress( Key& key ) {}
        virtual void keyRelease( Key& key ) {}
        
        virtual void mousePress( Mouse& key ) {}
        virtual void mouseRelease( Mouse& key ) {}
        virtual void mouseMove( Mouse& key ) {}
        virtual void mouseDrag( Mouse& key ) {}
        virtual void mouseScroll( Mouse& key ) {}
        virtual void mouseDoubleclick( Mouse& key ) {}
        
        virtual void touchDown( Touch& touch ) {}
        virtual void touchUp( Touch& touch ) {}
        virtual void touchMove( Touch& touch ) {}
        
        virtual void penPressure( Pen& pen ) {}
        virtual void penOrientation( Pen& pen ) {}
        virtual void penEnter( Pen& pen ) {}
        virtual void penLeave( Pen& pen ) {}

        virtual void widgetButton( ButtonWidget& button ) {}
        virtual void widgetSlider( SliderWidget& slider ) {}
        virtual void widgetCheckbox( CheckboxWidget& checkbox ) {}    
    
                
        virtual int run();
        virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&);
        virtual void accept(osgGA::GUIEventHandlerVisitor& v)   { v.visit(*this); };
    
    protected:
        Key    _keyEvent;
        Mouse  _mouseEvent;
        Pen    _penEvent;
        
};



class Scene {

    public:
      
        Scene();
        virtual ~Scene() {};

        int run();
        
        
        // Accessors
        //    
        int getWidth() { return _width; }
        int getHeight() { return _height; }
        void setBackgroundColor( const osg::Vec4& color );
        void setBackgroundColor( float r, float g, float b, float a=1.0f );
        osg::Vec3 getBackgroundColor();
        void setBackgroundAuto( bool bAuto );
        int getFrameNumber();
        double getSimulationTime();  //in sec from app launch
        void enableDefaultLighting();
        void disableDefaultLighting();
        void enableMultisampling( int val=4 );
        void disableMultisampling();
                

        // Scene Graph Hierarchy
        //
        virtual bool addChild( Node* node );
        virtual bool insertChild( unsigned int index, Node* node );
        virtual bool removeChild( Node* node );
        virtual bool replaceChild( Node* origNode, Node* newNode );
        unsigned int getNumChildren();
        virtual bool setChild( unsigned int index, Node* node );
        Node*        getChild( unsigned int index );
        bool         containsNode( Node* node );
        unsigned int getChildIndex( Node* node );
        
        // Scene Graph Geometry
        //
        virtual bool addDrawable( Drawable* drawable );
        virtual bool removeDrawable( Drawable* drawable );
        virtual bool replaceDrawable( Drawable* origDrawable, Drawable* newDrawable );
        unsigned int getNumDrawables();
        virtual bool setDrawable( unsigned int index, Drawable* drawable );
        Drawable*    getDrawable( unsigned int index );
        bool         containsDrawable( Drawable* drawable );
        unsigned int getDrawableIndex( Drawable* drawable );
        
        
        // App Events
        //
        virtual void update();
        virtual void draw();
        virtual void windowResized( int w, int h );
        virtual void addEventHandler( App* _app );

        // Inject Touch Events
        // 
        virtual void touchDown( float x, float y, int touchId );
        virtual void touchMove( float x, float y, int touchId );
        virtual void touchUp( float x, float y, int touchId );
        virtual void mousePress( float x, float y, int button );
        virtual void mouseDrag( float x, float y, int button );
        virtual void mouseRelease( float x, float y, int button );
              
        // OSG stuff
        //
        virtual osg::ref_ptr<osg::Camera> getOsgCamera() { return _camera; }
        virtual osg::ref_ptr<osg::PositionAttitudeTransform> getOsgXForm() { return _root; }
                
    
    protected:
        App* _app;
        unsigned int _width, _height;
        
        osg::ref_ptr<osgViewer::Viewer>               _viewer;
        osg::ref_ptr<osgViewer::GraphicsWindow>       _window;
        osg::ref_ptr<osg::Camera>                     _camera;
        osg::ref_ptr<osg::PositionAttitudeTransform>  _root;
        osg::ref_ptr<osg::Geode>                      _geode;
            
        Touch theMouseTouch;
        std::map<int,Touch*>  _activeTouches;  // active touches by id, map owns the objects
        std::map<int,TouchHit*>  _touchHits;   // intermediary touches, sorted by zIndex
                                                 // used in rayIntersectWidgets(...)

        zIndexWidgetVisitor  _zIndexVisitor;     // Scene graph visitor, see OSG visitor pattern.
                                                 // Sets the zIndex according to graph hierachy.
                               
        void initForGlutLegacy();
        void setupPixelCamera();
        void setupLookAtCamera();                                                              
        void zIndexWidgetTraversal();
        void rayIntersectWidgets( float x, float y );
        osg::Vec3d  rayIntersectWithXYPlane(float x, float y, Widget* widget);
        bool getPlaneLineIntersection(const osg::Vec4d& plane, 
                                      const osg::Vec3d& lineStart, const osg::Vec3d& lineEnd, 
                                      osg::Vec3d& isect);    
        
        virtual void touchDown( Touch* touch );
        virtual void touchMove( Touch* touch );
        virtual void touchUp( Touch* touch );
};


}
#endif

